package com.atguigu.tingshu.user.service.impl;
import java.util.*;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.common.constant.PublicConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.MongoUtil;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.UserPaidAlbumMapper;
import com.atguigu.tingshu.user.mapper.UserPaidTrackMapper;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackStatMqVo;
import com.atguigu.tingshu.vo.user.UserCollectVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.user.mapper.UserInfoMapper;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;








@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

	@Autowired
	private UserInfoMapper userInfoMapper;

	@Autowired
	private WxMaService wxMaService;

	@Autowired
	private RsaSigner rsaSigner;

	@Autowired
	private StringRedisTemplate redisTemplate;

	@Autowired
	private RabbitService rabbitService;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;
    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;
    @Autowired
    private MongoTemplate mongoTemplate;

	@Autowired
	private AlbumInfoFeignClient albumInfoFeignClient;

	/**
	 * 普通的token和生成的token的区别：
	 * 	1.普通的token
	 * @param code
	 * @return
	 */
	@Override
	public Map<String, Object> wxLogin(String code) {
		Map<String, Object> map = new HashMap<>();
		String openid = "";
		//todo 需要微信接口调用的客户端
		//1.判断code码是否存在
		if (StringUtils.isEmpty(code)){
			throw new GuiguException(201,"code不存在");
		}

		//2.调用微信服务端
		WxMaUserService userService = wxMaService.getUserService();
        WxMaJscode2SessionResult sessionInfo = null;
        try {
            sessionInfo = userService.getSessionInfo(code);
			openid = sessionInfo.getOpenid();
        } catch (WxErrorException e) {
            log.error("调用微信服务端失败:{}",e.getMessage());
			throw new GuiguException(201,"调用微信服务端异常");
        }

		
		String refreshTokenKey = RedisConstant.USER_LOGIN_REFRESH_KEY_PREFIX + openid;

		String jsonWebTokenFromRedis = redisTemplate.opsForValue().get(refreshTokenKey);
		if (!StringUtils.isEmpty(jsonWebTokenFromRedis)){
			Map<String, Object> map1 = new HashMap<>();
			map1.put("token",jsonWebTokenFromRedis);
			return map1;
		}

		//3.根据openId查询用户信息
		LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
		userInfoLambdaQueryWrapper.eq(UserInfo::getWxOpenId,openid);
		UserInfo userInfo = userInfoMapper.selectOne(userInfoLambdaQueryWrapper);

		if (userInfo == null){
			//todo
			//1.向user_info表中插入用户(注册用户信息)
			userInfo = new UserInfo();
			userInfo.setWxOpenId(openid);
			userInfo.setNickname(System.currentTimeMillis()+"glsx"+ UUID.randomUUID().toString().substring(0,4).replaceAll("-",""));
			userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
			userInfo.setIsVip(0); //不是vip
			userInfo.setVipExpireTime(new Date());

			int insert = userInfoMapper.insert(userInfo);
			log.info("注册用户:{}",insert>0?"success" : "fail");
			//todo
			//2.向tingshu_account表中的user_account表中插入用户账户(初始化用户账户余额)
			rabbitService.sendMessage(MqConst.EXCHANGE_USER,MqConst.ROUTING_USER_REGISTER,userInfo.getId().toString());
			log.info("用户微服务发送初始化用户账户余额消息：{}成功",userInfo.getId());
		}

//		定义一个载荷
		//4.生成一个token值返回给前端
		String token = getJsonWebToken(userInfo.getId(), openid);
		map.put("token",token); //token值
		map.put("refreshToken",token);

		// Redis常用的数据类型有5中(String(足矣)  List Set Zset Hash(Map))
		//5.将jsonWebToken存放到Redis中
		String accessTokenKey = RedisConstant.USER_LOGIN_KEY_PREFIX + openid;


		/*
			给用户登陆的信息设置一个过期时间
			长 短
			短：频繁登录  优点：安全一些（攻击的时间窗口较小）
			长：不用频繁登录 缺点：不太安全（攻击的时间窗口较大）

			引入一个token(1.为了提高用户的体验  2.安全)
			双token机制 accessToken(时间段，一般在分钟或者小时级别)  和 RefreshToken(时间长，在天或者月)
		 */
		redisTemplate.opsForValue().set(accessTokenKey,token,10, TimeUnit.DAYS);//短一点
		redisTemplate.opsForValue().set(refreshTokenKey,token,10, TimeUnit.DAYS);//长一点

		//6.返回给前端
		return map;
	}



	private String getJsonWebToken(Long userId, String openid) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("id", userId);
		jsonObject.put("openId", openid);
		//jwt方式生成
		Jwt jsonWebToken = JwtHelper.encode(jsonObject.toString(), rsaSigner);
		String encoded = jsonWebToken.getEncoded();
		return encoded;
	}



	@Override
	public Map<String, Object> getNewAccessToken() {
		Map<String, Object> result = new HashMap<>();
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

		HttpServletRequest request = requestAttributes.getRequest();

		String token = request.getHeader("token");
		if (StringUtils.isEmpty(token)){
			throw new GuiguException(201,"之前没有登陆过");
		}

		Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(PublicConstant.PUBLIC_KEY));
		String claims = jwt.getClaims();
		Map map = JSONObject.parseObject(claims, Map.class);
		Object openId = map.get("openId");
		Object userId = map.get("id");


		//1.从Redis中获取refreshToken
		String refreshTokenKey = RedisConstant.USER_LOGIN_REFRESH_KEY_PREFIX + openId;
		String accessTokenKey = RedisConstant.USER_LOGIN_KEY_PREFIX + openId;
		//2.判断Redis中是否存在RefreshToken

		String refreshToken = redisTemplate.opsForValue().get(refreshTokenKey);

		if (!StringUtils.isEmpty(refreshToken)){

			String jsonWebToken = getJsonWebToken(Long.parseLong(userId.toString()), String.valueOf(openId.toString()));
			redisTemplate.opsForValue().set(accessTokenKey,jsonWebToken,20,TimeUnit.MINUTES);//测试环境
			redisTemplate.opsForValue().set(refreshTokenKey,jsonWebToken,180,TimeUnit.DAYS);//测试环境
			result.put("token",jsonWebToken);
			return result;
		}else {
//			去登陆
			result.put("1","v");
			return result;
		}
		//2.1 如果没有，refreshToken也过期了  重新去登录

		//2.2如果有，生成一个新令牌返回给前端
	}

	@Override
	public void updateUser(UserInfoVo userInfoVo) {
		//1.查询用户信息
		Long userId = AuthContextHolder.getUserId();
		UserInfo userInfo = userInfoMapper.selectById(userId);
		if (userId == null){
			throw new GuiguException(201,"用户信息不存在");
		}
		userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
		userInfo.setNickname(userInfoVo.getNickname());

		userInfoMapper.updateById(userInfo);
	}

	@Override
	public UserInfoVo getUserInfo(Long userId) {
		UserInfo userInfo = getById(userId);
		if (userInfo == null){
			throw new GuiguException(201,"该用户不存在");
		}
		UserInfoVo userInfoVo = new UserInfoVo();
		BeanUtils.copyProperties(userInfo,userInfoVo);
		return userInfoVo;
	}

	@Override
	public Map<Long, String> getUserPaidAlbumTrack(Long userId, Long albumId) {
		LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
		userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getUserId,userId);
		userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getAlbumId,albumId);

		List<UserPaidTrack> userPaidTracks = userPaidTrackMapper.selectList(userPaidTrackLambdaQueryWrapper);
		Map<Long, String> map = new HashMap<>();
		userPaidTracks.forEach(v->{
			map.put(v.getTrackId(),"1");
		});
//		Map<Long, String> map = userPaidTracks.stream().collect(Collectors.toMap(UserPaidTrack::getTrackId, v -> "1"));
		return map;
	}

	@Override
	public Boolean getUserPaidAlbum(Long userId, Long albumId) {
		LambdaQueryWrapper<UserPaidAlbum> userPaidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
		userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getUserId,userId);
		userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getAlbumId,albumId);
		UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(userPaidAlbumLambdaQueryWrapper);
        return userPaidAlbum != null;

    }

	@Override
	public Boolean collect(Long trackId) {
		Long userId = AuthContextHolder.getUserId();

		//1.构建条件对象
		Criteria criteria = Criteria.where("userId").is(userId).and("trackId").is(trackId);

		//2.构建查询对象
		Query query = new Query(criteria);

		//3.开始查询
		String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_COLLECT, userId);
		UserCollect userCollect = mongoTemplate.findOne(query, UserCollect.class,collectionName);
		if (userCollect == null){
			// 收藏声音
			// 插入收藏对象到MongoDB
			userCollect = new UserCollect();
			userCollect.setId(ObjectId.get().toString());
			userCollect.setUserId(userId);
			userCollect.setTrackId(trackId);
			userCollect.setCreateTime(new Date());
			mongoTemplate.save(userCollect,collectionName);

			//TODO 更新Mysql声音的收藏量
			TrackStatMqVo trackStatMqVo = prepareTrackStatMqDto(null, userCollect.getTrackId(), SystemConstant.TRACK_STAT_COLLECT, 1);
			rabbitService.sendMessage(MqConst.EXCHANGE_TRACK,MqConst.ROUTING_TRACK_STAT_UPDATE,JSONObject.toJSONString(trackStatMqVo));
			return true;  //收藏过
		}else {
			//取消收藏声音
			mongoTemplate.remove(query, UserCollect.class,collectionName);
			TrackStatMqVo trackStatMqVo = prepareTrackStatMqDto(null, userCollect.getTrackId(), SystemConstant.TRACK_STAT_COLLECT, -1);
			rabbitService.sendMessage(MqConst.EXCHANGE_TRACK,MqConst.ROUTING_TRACK_STAT_UPDATE,JSONObject.toJSONString(trackStatMqVo));
			//TODO 更新Mysql声音的收藏量
			return false;
		}


	}

	@Override
	public TrackStatMqVo prepareTrackStatMqDto(Long albumId, Long trackId, String trackStatPlay, int count) {
		TrackStatMqVo trackStatMqVo = new TrackStatMqVo();
		trackStatMqVo.setBusinessNo(UUID.randomUUID().toString().replace("-","")); //消息去重
		trackStatMqVo.setAlbumId(albumId);
		trackStatMqVo.setTrackId(trackId);
		trackStatMqVo.setStatType(trackStatPlay);
		trackStatMqVo.setCount(count);

		return trackStatMqVo;
	}

	@Override
	public Boolean isCollect(Long trackId) {
		Long userId = AuthContextHolder.getUserId();

		//1.构建条件对象
		Criteria criteria = Criteria.where("userId").is(userId).and("trackId").is(trackId);

		//2.构建查询对象
		Query query = new Query(criteria);

		String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_COLLECT, userId);
		long count = mongoTemplate.count(query, UserCollect.class, collectionName);

		return count > 0;

	}

	@Override
	public Boolean isSubscribe(Long albumId) {
		Long userId = AuthContextHolder.getUserId();

		//1.构建条件对象
		Criteria criteria = Criteria.where("userId").is(userId).and("albumId").is(albumId);

		//2.构建查询对象
		Query query = new Query(criteria);


		long count = mongoTemplate.count(query, UserSubscribe.class, MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_SUBSCRIBE, userId));
		return count > 0;
	}

	@Override
	public IPage<UserCollectVo> findUserCollectPage(IPage<UserCollectVo> page) {
		Long userId = AuthContextHolder.getUserId();
		//1.构建条件对象
		Criteria criteria = Criteria.where("userId").is(userId);
		//2.构建查询对象
		Query query = new Query(criteria);
		//3.构建分页和排序条件对象[mp中分页查询对MySQL查询有效]

		Sort sort = Sort.by(Sort.Order.desc("updateTime"));

		//from:(pn - 1)*size  size
		PageRequest pageAndSort = PageRequest.of((int) ((page.getCurrent() - 1) * page.getSize()), (int) page.getSize(), sort);
		query.with(pageAndSort);

		//4.查询总记录数
		long count = mongoTemplate.count(query.limit(-1), UserCollect.class, MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_COLLECT, userId));

		//5.查询数据
		List<UserCollect> userCollectList = mongoTemplate.find(query, UserCollect.class, MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_COLLECT, userId));

		List<Long> trackIdList = userCollectList.stream().map(UserCollect::getTrackId).collect(Collectors.toList());
		//6.根据声音id集合 查询【声音对象集合】

		Result<List<TrackListVo>> trackListVoResult = albumInfoFeignClient.getTrackListByIds(trackIdList);
		List<TrackListVo> trackListVos = trackListVoResult.getData();
		if (CollectionUtils.isEmpty(trackListVos)){
			throw new GuiguException(201,"远程查询专辑微服务获取声音集合失败");
		}
		//7.将声音对象的列表集合转成声音Map
		Map<Long, TrackListVo> trackListVoMap = trackListVos.stream().collect(Collectors.toMap(TrackListVo::getTrackId, v -> v));

		List<UserCollectVo> userCollectVoList = userCollectList.stream().map(userCollect -> {
			UserCollectVo userCollectVo = new UserCollectVo();
			TrackListVo trackListVo = trackListVoMap.get(userCollect.getTrackId());
			userCollectVo.setAlbumId(trackListVo.getAlbumId()); //收藏声音对应的专辑Id
			userCollectVo.setTrackId(userCollect.getTrackId());
			userCollectVo.setCreateTime(userCollect.getCreateTime());
			userCollectVo.setTrackTitle(trackListVo.getTrackTitle());  //收藏声音标题
			userCollectVo.setCoverUrl(trackListVo.getCoverUrl());   //收藏声音的封面
			return userCollectVo;
		}).collect(Collectors.toList());
		return page.setRecords(userCollectVoList).setTotal(count);
	}

	@Override
	public void updateExpireVip() {
		//1.查询过期的vip
		LambdaQueryWrapper<UserInfo> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(UserInfo::getIsVip,1);
		wrapper.lt(UserInfo::getVipExpireTime,new Date());
		wrapper.eq(UserInfo::getIsDeleted,0);

		List<UserInfo> userInfos = userInfoMapper.selectList(wrapper);
		userInfos.stream().forEach(userInfo -> {
			userInfo.setIsVip(0);
			userInfoMapper.updateById(userInfo);
		});
	}
}
